package de.hwrberlin.remo.algorithm.forwardchaining;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import de.hwrberlin.remo.algorithm.AlgorithmInitializer;
import de.hwrberlin.remo.algorithm.InferenzAlgorithm;
import de.hwrberlin.remo.algorithm.steps.SetStateStep;
import de.hwrberlin.remo.algorithm.steps.ValidateRuleStep;
import de.hwrberlin.remo.model.ElementState;
import de.hwrberlin.remo.model.KnowledgeBase;
import de.hwrberlin.remo.model.Rule;

/**
 * This class implements the forward chaining algorithm, which
 * uses an linear list to represent all rules (and facts) of
 * the knowledge base.
 * 
 * @author Mario Sänger (IT 09)
 * @version 1.0
 *
 */
public class ListBasedForwardChaining extends InferenzAlgorithm {

	@Override
	public void run(KnowledgeBase base) {
		Logger logger = getLogger();

		logger.debug("Starting forward chaining algorithm");
		logger.debug("Initialize concluded rules list");

		List<Rule> concludedRules = new ArrayList<Rule>();
		List<Rule> ruleList = base.getRules();
		
		boolean conclusionChanged = true;
		
		// Run until no new conclusions can be made
		while(conclusionChanged) {
			conclusionChanged = false;
			
			logger.debug("Entering main loop");
			
			// Check all rules of the rule list
			for(Rule rule : ruleList) {
				
				logger.debug("Validating rule '" + rule.getName() + "'");
				
				// Already concluded rules can be ignored
				if(concludedRules.contains(rule)) {
					logger.debug("Rule '" + rule.getName() + "' was already concluded");
					continue;
				}
				
				// Validate rule
				ValidateRuleStep validateStep = new ValidateRuleStep(rule, getUserInterface(), editingHelper);
				validateStep.perform();
				
				fireStepFinishedEvent(validateStep);
				
				// Calculate new state
				logger.debug("Calculating new state of rule '" + rule.getName() + "'");
				ElementState newState = rule.checkRule();
				
				// Set new state (if new state is not unknown)
				if(newState != ElementState.UNKNOWN) {
					logger.debug("New state of rule '" + rule.getName() + "' is " + newState.getName());
					
					SetStateStep setStep = new SetStateStep(editingHelper, rule, newState);
					setStep.perform();
					fireStepFinishedEvent(setStep);

					logger.debug("Adding rule '" + rule.getName() + "' to concluded rules list");
					concludedRules.add(rule);
					conclusionChanged = true;
				} else {
					logger.debug("New state of rule '" + rule.getName() + "' is unknown");
				}
			}
			
			logger.debug("Finished main loop");
			logger.debug("Concluded rules list now constains " + concludedRules.size() + " element(s)");
			logger.debug("Found new concluded rules during this loop: " + conclusionChanged);
			
			if(!conclusionChanged) {
				logger.debug("-> Forward chaining algorithm finished");
			}
		}
	}

	@Override
	public AlgorithmInitializer getInitializer() {
		// No initializer is needed
		return null;
	}
}